package top.wilsonlv.jaguar.commons.web.swagger;

import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.OAuthBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author lws
 * @since 2019/4/22.
 */
@Slf4j
@Configuration
@EnableKnife4j
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "jaguar.web.swagger", name = "enable", havingValue = "true", matchIfMissing = true)
public class SwaggerConfig {

    private final SwaggerProperties swaggerProperties;

    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${spring.application.version}")
    private String applicationVersion;

    @Bean
    public Docket platformApi() {
        log.info("enable swagger config");
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .groupName(applicationName)
                .apiInfo(apiInfo());

        if (swaggerProperties.getOAuth2GrantType() != null) {
            OAuth oAuth = new OAuthBuilder().name("oauth2").grantTypes(grantTypes()).build();

            SecurityReference securityReference = new SecurityReference("oauth2", new AuthorizationScope[0]);
            SecurityContext securityContext = new SecurityContext(Collections.singletonList(securityReference),
                    PathSelectors.ant("/api/**"), null, null);

            docket.securitySchemes(Collections.singletonList(oAuth))
                    .securityContexts(Collections.singletonList(securityContext));
        }

        return docket
                .select()
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(swaggerProperties.getTitle())
                .description(swaggerProperties.getDescription())
                .contact(new Contact(swaggerProperties.getContactName(), swaggerProperties.getContactUrl(), swaggerProperties.getContactEmail()))
                .version(applicationVersion)
                .license(swaggerProperties.getLicense())
                .licenseUrl(swaggerProperties.getLicenseUrl())
                .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
                .build();
    }

    private List<GrantType> grantTypes() {
        String tokenName = "access_token";

        //授权方式
        List<GrantType> grantTypes = new ArrayList<>();
        switch (swaggerProperties.getOAuth2GrantType()) {
            case IMPLICIT_GRANT: {
                //简单模式 implicit
                ImplicitGrant implicitGrant = new ImplicitGrant(
                        new LoginEndpoint(swaggerProperties.getAuthorizationUrl()), tokenName);
                grantTypes.add(implicitGrant);
                break;
            }
            case AUTHORIZATION_CODE_GRANT: {
                //授权码模式 AuthorizationCodeGrant
                TokenRequestEndpoint tokenRequestEndpoint = new TokenRequestEndpoint(swaggerProperties.getAuthorizationUrl(),
                        swaggerProperties.getClientId(), swaggerProperties.getClientSecret());
                TokenEndpoint tokenEndpoint = new TokenEndpoint(swaggerProperties.getAuthTokenUrl(), tokenName);
                AuthorizationCodeGrant authorizationCodeGrant = new AuthorizationCodeGrant(tokenRequestEndpoint, tokenEndpoint);
                grantTypes.add(authorizationCodeGrant);
                break;
            }
            case CLIENT_CREDENTIALS_GRANT: {
                //客户端模式 client credentials
                ClientCredentialsGrant clientCredentialsGrant = new ClientCredentialsGrant(swaggerProperties.getAuthTokenUrl());
                grantTypes.add(clientCredentialsGrant);
                break;
            }
            case PASSWORD_CREDENTIALS_GRANT: {
                //密码模式
                ResourceOwnerPasswordCredentialsGrant passwordCredentialsGrant =
                        new ResourceOwnerPasswordCredentialsGrant(swaggerProperties.getAuthTokenUrl());
                grantTypes.add(passwordCredentialsGrant);
                break;
            }
            default:
        }
        return grantTypes;
    }

}